package com.learn.security.oauth2.auth.server.config.security;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.learn.security.oauth2.auth.server.config.security.jackson.LoginUserDeserializer;
import com.learn.security.oauth2.orm.SysUser;
import lombok.Data;
import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.time.LocalDate;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 登录用户信息
 *
 * @author knight
 */
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonDeserialize(using = LoginUserDeserializer.class)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
		isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true)
public class LoginUser implements UserDetails, CredentialsContainer {

	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

	/** 用户id */
	private String id;

	/** 用户名 */
	private String username;

	/** 密码 */
	private String password;

	/** 头像 */
	private String avatar;

	/** 电话 */
	private String phone;

	/** 邮箱 */
	private String email;

	/** 性别 */
	private Integer gender;

	/** 出生日期 */
	@JsonSerialize(using = LocalDateSerializer.class)
	private LocalDate birth;

	/** 权限列表 */
	private Set<GrantedAuthority> authorities;

	/** 是否启用 */
	private boolean enabled;

	/** 是否未过期 */
	private boolean accountNonExpired;

	/** 是否非锁定账户 */
	private boolean accountNonLocked;

	/** 凭证是否未过期 */
	private boolean credentialsNonExpired;

	/**
	 * 登录用户
	 * @param sysUser 系统用户
	 * @param roleSet 角色集合
	 * @param permSet 权限集合
	 */
	public LoginUser(SysUser sysUser, Set<String> roleSet, Set<String> permSet) {
		this.id = sysUser.getId();
		this.username = sysUser.getSuUsername();
		this.password = sysUser.getSuPassword();
		this.avatar = sysUser.getSuAvatar();
		this.phone = sysUser.getSuPhone();
		this.email = sysUser.getSuEmail();
		this.gender = sysUser.getSuGender();
		this.birth = sysUser.getSuBirth();
		this.accountNonExpired = sysUser.getSuExpired();
		this.accountNonLocked = sysUser.getSuLocked();
		this.credentialsNonExpired = sysUser.getSuPasswordExpired();
		this.enabled = sysUser.getSuEnable();
		addGrantedAuthority(roleSet, permSet);
	}

	public LoginUser(String id, String username, String password, String avatar, String phone, String email,
			Integer gender, LocalDate birth, Set<? extends GrantedAuthority> authorities, boolean enabled,
			boolean accountNonExpired, boolean accountNonLocked, boolean credentialsNonExpired) {
		this.id = id;
		this.username = username;
		this.password = password;
		this.avatar = avatar;
		this.phone = phone;
		this.email = email;
		this.gender = gender;
		this.birth = birth;
		this.authorities = authorities.stream().sorted(Comparator.comparing(GrantedAuthority::getAuthority))
				.collect(Collectors.toCollection(LinkedHashSet::new));
		this.enabled = enabled;
		this.accountNonExpired = accountNonExpired;
		this.accountNonLocked = accountNonLocked;
		this.credentialsNonExpired = credentialsNonExpired;
	}

	/**
	 * 添加授权 为什么要排序？ 参考：https://github.com/spring-projects/spring-security/issues/981
	 * @param roleSet 角色组
	 * @param permSet 权限组
	 */
	public void addGrantedAuthority(Set<String> roleSet, Set<String> permSet) {
		Stream<GrantedAuthority> roleStream = roleSet.stream().map(SimpleGrantedAuthority::new);
		Stream<GrantedAuthority> permStream = permSet.stream().map(SimpleGrantedAuthority::new);
		this.authorities = Stream.concat(roleStream, permStream)
				.sorted(Comparator.comparing(GrantedAuthority::getAuthority))
				.collect(Collectors.toCollection(LinkedHashSet::new));
	}

	/**
	 * 认证完成后删除凭证
	 */
	@Override
	public void eraseCredentials() {
		this.password = null;
	}

}
